/**
  ******************************************************************************
  * @file    mg_ble_gatt.c
  * @author  
  * @version V1.2
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2020 Shanghai Macrogiga Electronics</center></h2>
  *
  ******************************************************************************
  */
#include <string.h>
#include "mg_driver.h"
#include "mg_ble_control.h"
#include "mg_ble_control_master.h"

void att_rsp(pdu_hdr_t hdr, u8 * attData);


//user's call back api
void ser_execute_write(void);
void ser_prepare_write(u16 handle, u8* attValue, u16 attValueLen, u16 att_offset);
void att_server_rdByGrType( u8 pdu_type, u8 attOpcode, u16 st_hd, u16 end_hd, u16 att_type);
void server_rd_rsp(u8 attOpcode, u16 attHandle, u8 pdu_type);
void ser_write_rsp(u8 pdu_type/*reserved*/, u8 attOpcode/*reserved*/, 
                   u16 att_hd, u8* attValue/*app data pointer*/, u8 valueLen_w/*app data size*/);
u8* getDeviceInfoData(u8* len);
u8 GetCharListDim(void);

typedef struct ble_character16{
    u16 type16;        //type2
    u16 handle_rec;    //handle
    u8  characterInfo[5];//protperty1 - handle2 - uuid2
    u8  uuid128_idx; //0xff means uuid16,other is idx of uuid128
}BLE_CHAR;

typedef struct ble_UUID128{
    u8  uuid128[16];//protperty1 - handle2 - uuid2
}BLE_UUID128;

extern const BLE_CHAR AttCharList[];
extern const BLE_UUID128 AttUuid128List[];

typedef struct att_incl_desc_t
{
    /// start handle value of included service
    u16 start_hdl;
    /// end handle value of included service
    u16 end_hdl;
    /// attribute value UUID
    u16 uuid;
} att_incl_desc;

#define chanId         (0x0004)
#define UUID16_FORMAT  0xff

///////////////////////////////////////////////////////////////////////////////////////////////////////
  
void att_server_rdByGrTypeRspPrimaryService(u8 pdu_type, u16 start_hd, u16 end_hd, u8*uuid, u8 uuidlen)
{
    u8 len;
    u8 _attData1[27];
    u8 i;
    pdu_hdr_t hdr;
    
    len = 0;
        
    _attData1[len++] = 0;
    _attData1[len++] = 0;
    _attData1[len++] = chanId & 0xFF;
    _attData1[len++] = chanId >> 8;
    _attData1[len++] = ATT_RD_BY_GROUP_TYPE_RSP;
    _attData1[len++] = 6+(uuidlen-2); //for uuid128 +14
        
    _attData1[len++] = start_hd;  //start HD
    _attData1[len++] = 0;
    _attData1[len++] = end_hd;  //END_HD
    _attData1[len++] = 0;
        
    //uuid128
    for(i = 0 ; i < uuidlen ; i++)
    {
        _attData1[len++] = uuid[i];
    }        
        
    _attData1[0] = len - 4;
    
    //data header
    /*
     * LLID : 2, NESSN : 1, SN : 0, MD : 0, PDU-Length : 18 
    */
    hdr.type = (pdu_type & 0x03);
    hdr.len = len;

    //to l2cap
    att_rsp(hdr, _attData1);
}

/*static*/ u8 att_enable_default_device_info = 0;

void att_server_rdByGrTypeRspDeviceInfo(u8 pdu_type)
{
    u8 len;
    u8 _attData1[27];
    pdu_hdr_t hdr;
    
    att_enable_default_device_info = 1;//enable the default device info
    
    len = 0;
        
    _attData1[len++] = 0;
    _attData1[len++] = 0;
    _attData1[len++] = chanId & 0xFF;
    _attData1[len++] = chanId >> 8;
    _attData1[len++] = ATT_RD_BY_GROUP_TYPE_RSP;
    _attData1[len++] = 6;
        
    _attData1[len++] = 1;
    _attData1[len++] = 0;
    _attData1[len++] = 6;  //END_HD
    _attData1[len++] = 0;
    _attData1[len++] = 0;
    _attData1[len++] = 0x18;

    _attData1[len++] = 7;
    _attData1[len++] = 0;
    _attData1[len++] = 0x0f;//END_HD
    _attData1[len++] = 0;
    _attData1[len++] = 0x0a;
    _attData1[len++] = 0x18;//device info;
    
    _attData1[0] = len - 4;
    
    //data header
    /*
     * LLID : 2, NESSN : 1, SN : 0, MD : 0, PDU-Length : 18 
    */
    hdr.type = (pdu_type & 0x03);
    hdr.len = len;

    //to l2cap
    att_rsp(hdr, _attData1);
}


///empty packet
void att_empPk(u8 pdu_type)
{
    return;//default is the empPk, so forget it. by zhouyi
}

///error handle
void att_notFd(u8 pdu_type, u8 attOpcode, u16 attHd)
{
    att_ErrorFd_eCode(pdu_type, attOpcode, attHd, ATT_ERR_ATTR_NOT_FOUND);
}

///read request handle
void att_server_rd( u8 pdu_type, u8 attOpcode, u16 att_hd, u8* attValue, u8 datalen )
{
    u16 l2cap_len;
    u8 l2cap_pdu[27] = {0};
    u8 counter;
    pdu_hdr_t hdr;

    if(datalen > 22) datalen = 22;
    hdr.type = (pdu_type & 0x03);
    hdr.len = 5 + datalen; //l2cap_length + chanid + opcode + datalen

    //pdu
    l2cap_len = 1 + datalen; 
    l2cap_pdu[0] = l2cap_len & 0xFF;
    l2cap_pdu[1] = l2cap_len >> 8;
    l2cap_pdu[2] = chanId & 0xFF;
    l2cap_pdu[3] = chanId >> 8;
    l2cap_pdu[4] = attOpcode+1;//ATT_READ_RSP;
    for(counter = 0; counter < datalen; counter++){
        l2cap_pdu[5+counter] = attValue[counter];
    }
    //to l2cap
    att_rsp(hdr, l2cap_pdu);
    return;
}

extern u16 AttMtuSize;//default = 23

void att_exMtuReq( u8 pdu_type, u8 attOpcode, u16 clientMtu )
{   
//    u8 _attData1[7] = {0x03,0x00, 0x04,0x00, ATT_EXCHANGE_MTU_RSP, 0x17,0x00};
    u8 _attData1[7] = {0x03,0x00, 0x04,0x00, ATT_EXCHANGE_MTU_RSP, 247,0x00};
    pdu_hdr_t hdr;
    
    _attData1[5] = (LongPktMaxTxOctets - 4) & 0xff;
    _attData1[6] = ((LongPktMaxTxOctets - 4) >> 8) & 0xff;
    
    //data header
    /*
    * LLID : 2, NESSN : 1, SN : 1, MD : 0, PDU-Length : 7 
    */
    hdr.type = (pdu_type & 0x03);
    hdr.len = 7;
    
    att_rsp(hdr, _attData1);
    
    //save MTU size
    AttMtuSize = clientMtu;
    if(clientMtu > (LongPktMaxTxOctets - 4))AttMtuSize = LongPktMaxTxOctets - 4;
}

u8 is_att_LongPktDataEnabled(void)
{
    return (AttMtuSize > 23) ? 1 : 0;
}

u8 att_get_sconn_notifyLongPktMinSize(void)
{
    return 25;
}

u8 att_get_sconn_notifyLongPktMaxSize(void)
{
    return AttMtuSize - 3;
}

u8 att_get_sconn_indicationLongPktMinSize(void)
{
    return 25;
}

u8 att_get_sconn_indicationLongPktMaxSize(void)
{
    return AttMtuSize - 3;
}

u8 att_get_att_server_rdLongPktMinSize(void)
{
    return 27;
}

u8 att_get_att_server_rdLongPktMaxSize(void)
{
    return AttMtuSize - 1;
}

void ser_execute_write_rsp(u8* l2cap)
{
    pdu_hdr_t hdr;
    
    hdr.type = 2;
    hdr.len = 5;
    
    l2cap[2]  = 1;
    l2cap[2+4] = ATT_EXECUTE_WR_RSP;
    att_rsp(hdr, l2cap+2);
    
    ser_execute_write();
}

void ser_prepare_write_rsp(u8 *all_hd_pdu/*including header*/)
{
    pdu_hdr_t hdr;
    u16 offset = all_hd_pdu[10];
    u16 len = all_hd_pdu[3];
    u16 handle = all_hd_pdu[8];
    
    handle <<= 8;
    handle |= all_hd_pdu[7];
    
    len <<= 8;
    len |= all_hd_pdu[2];
    len -= 5;
    
    offset <<= 8;
    offset |= all_hd_pdu[9];
    
    hdr.type = 2;
    hdr.len = all_hd_pdu[1];
    
    all_hd_pdu[2+4] = ATT_PREPARE_WR_RSP;
    att_rsp(hdr, all_hd_pdu+2);
    
    ser_prepare_write(handle,all_hd_pdu+11,len,offset);
}

void att_add_more_character_rec(pdu_hdr_t *_hdr,u8 *_attData,u16 end_hd,u8 cha_idx0)
{
    u8 k,i,offset = 13;
    
    for(k = 0 ; k < 2 ; k ++)
    {
        if(cha_idx0 >= GetCharListDim())return; //end
        
        //get the extra one rec if any
        if((AttCharList[cha_idx0].type16 == 0x2803) && 
           (AttCharList[cha_idx0].uuid128_idx == UUID16_FORMAT) && 
           (AttCharList[cha_idx0].handle_rec <= end_hd) )
        {
            _attData[offset++] = AttCharList[cha_idx0].handle_rec & 0xFF;// char declaration handle
            _attData[offset++] = AttCharList[cha_idx0].handle_rec >> 8;
            for(i = 0; i < 5; i++){
                _attData[offset++] = AttCharList[cha_idx0].characterInfo[i];
            }

            _hdr->len += 7;
            _attData[0] += 7; //l2cap length
            
            cha_idx0 ++;        
        }        
    }    
}

///read by type resquest handle
void att_server_rdByType(u8 pdu_type, u8 attOpcode, u16 st_hd, u16 end_hd, u16 att_type)
{
    u8 _attData1[27];
    u8 len;
    u16 l2cap_len;
    u8 i;

    u8 cha_num;
    pdu_hdr_t hdr;
                        
    if(att_type == DEVICE_NAME_UUID)
    {
        u8  d_len;
        u8* ble_name = getDeviceInfoData(&d_len);//[] = "mg-led-ble";
        
        hdr.type = (pdu_type & 0x03);
        hdr.len = 0;//reset
        
        len = 2 + d_len;//sizeof(ble_name);
        if(len > 21)len = 21;
        l2cap_len = 2 + len;
        
        _attData1[0] = l2cap_len & 0xFF;  
        _attData1[1] = l2cap_len >> 8;
        _attData1[2] = chanId & 0xFF;
        _attData1[3] = chanId >> 8;
        _attData1[4] = ATT_RD_BY_TYPE_RSP;
        _attData1[5] = len;	
        _attData1[6] = 0x04;//  handle
        _attData1[7] = 0x00;
        memcpy(_attData1+8,ble_name,len-2);
        
        hdr.len = 4 + l2cap_len;
        
        att_rsp(hdr, _attData1);
            
        return;
    }
    else if((att_type == GATT_CHARACTER_UUID) && (st_hd == 0x07) && att_enable_default_device_info) //device info
    {
        hdr.type = (pdu_type & 0x03);
        hdr.len = 0;//reset
        
        len = 7;
        l2cap_len = len*3 + 2;
        
        _attData1[0] = l2cap_len & 0xFF;  
        _attData1[1] = l2cap_len >> 8;
        _attData1[2] = chanId & 0xFF;
        _attData1[3] = chanId >> 8;
        _attData1[4] = ATT_RD_BY_TYPE_RSP;
        _attData1[5] = len;	
        
        i = 6; 
        
        //hard code here for some info
        _attData1[6] = 0x08;//  handle
        _attData1[7] = 0;
        _attData1[8] = ATT_CHAR_PROP_RD;
        _attData1[9] = 0x09;
        _attData1[10] = 0;
        _attData1[11] = 0x29;//manufacture info
        _attData1[12] = 0x2a;
        
        i = 13;

        _attData1[i++] = 0x0a;//  handle
        _attData1[i++] = 0;
        _attData1[i++] = ATT_CHAR_PROP_RD;
        _attData1[i++] = 0x0b;
        _attData1[i++] = 0;
        _attData1[i++] = 0x26; //firmware version
        _attData1[i++] = 0x2a;
       
        _attData1[i++] = 0x0e;//  handle
        _attData1[i++] = 0;
        _attData1[i++] = ATT_CHAR_PROP_RD;
        _attData1[i++] = 0x0f;
        _attData1[i++] = 0;
        _attData1[i++] = 0x28;//software version
        _attData1[i++] = 0x2a;
        
        hdr.len = 4 + l2cap_len;
            
        att_rsp(hdr, _attData1);

        return;
    }
                       
    if(end_hd > 0xFF)end_hd = 0xFF;//for some safe reason(this will lead time out problem)
    
    for(cha_num=0; cha_num<GetCharListDim()/*sizeof(AttCharList)/sizeof(AttCharList[0])*/; cha_num++)
    {
        if((att_type == AttCharList[cha_num].type16) && (AttCharList[cha_num].handle_rec >= st_hd) &&(AttCharList[cha_num].handle_rec <= end_hd))
        {
            if(att_type == 0x2802 /*TYPE_INC*/)
            {
                _attData1[0] = 0x0a;  
                _attData1[1] = 0;
                _attData1[2] = chanId & 0xFF;
                _attData1[3] = chanId >> 8;
                _attData1[4] = ATT_RD_BY_TYPE_RSP;
                _attData1[5] = 8;
                _attData1[6] = AttCharList[cha_num].handle_rec & 0xFF;// char declaration handle
                _attData1[7] = AttCharList[cha_num].handle_rec >> 8;
                for(i = 0; i < 5; i++){
                    _attData1[8+i] = AttCharList[cha_num].characterInfo[i];
                } 
                _attData1[13] = AttCharList[cha_num].uuid128_idx; //I just share the memory, NOT the uuid idx
                
                hdr.type = (pdu_type & 0x03);
                hdr.len = 14;
                
            }
            else//default character type
            {
                _attData1[0] = 9;  
                _attData1[1] = 0;
                _attData1[2] = chanId & 0xFF;
                _attData1[3] = chanId >> 8;
                _attData1[4] = ATT_RD_BY_TYPE_RSP;
                _attData1[5] = 7;
                _attData1[6] = AttCharList[cha_num].handle_rec & 0xFF;// char declaration handle
                _attData1[7] = AttCharList[cha_num].handle_rec >> 8;
                
                for(i = 0; i < 5; i++){
                    _attData1[8+i] = AttCharList[cha_num].characterInfo[i];
                }                    
                    //data header
                    /*
                     * LLID : 2, NESSN : 1, SN : 0, MD : 0, PDU-Length : 18 
                     */
                hdr.type = (pdu_type & 0x03);
                hdr.len = 13;
                
                //check for uuid128
                if(AttCharList[cha_num].uuid128_idx != UUID16_FORMAT) //so uuid 128 used
                {
                    for(i = 0; i < 16; i++){
                        _attData1[8+3+i] = AttUuid128List[AttCharList[cha_num].uuid128_idx].uuid128[i];
                    }                    
                    hdr.len = 13+14;
                    _attData1[0] = 23; //l2cap length
                    _attData1[5] = 7+14;
                }
                else//add two more rec if any
                {
                    att_add_more_character_rec(&hdr,(u8*)_attData1,end_hd,cha_num+1);
                }
            }
            
            att_rsp(hdr, _attData1);
            
            return;
        }
    }
  
    att_notFd( pdu_type, attOpcode, st_hd );/// error handle
}

//return 1 means found, porting api
su32 GetPrimaryServiceHandle(u16 hd_start, u16 hd_end,
                            u16 uuid16,   
                            u16* hd_start_r,u16* hd_end_r);

//find by type value for rongyao phone 20170110 --------------
//this is just used for BQB testing, it doesn't work for none-16bit uuid
void att_server_findByType(u8 pdu_type, u8 attOpcode, u16 st_hd, u16 end_hd,u16 AttType, u16 Value)
{
    pdu_hdr_t hdr;
    u8 _attData1[10];
    
    hdr.type = pdu_type;
    hdr.len  = 9;
    
    _attData1[0] = 5;  //len
    _attData1[1] = 0;
    _attData1[2] = 4;//cid
    _attData1[3] = 0;
    _attData1[4] = attOpcode+1;//rsp
    _attData1[5] = 1; //handle start, hard code
    _attData1[6] = 0;
    _attData1[7] = 1; //handle end
    _attData1[8] = 0;
    
    if(AttType == GATT_PRIMARY_SERVICE_UUID)
    {
        u16 hd_s,hd_e;
#if 0        
        if((u16)0x1801 == Value)//GattProfile
        {            
            att_notFd( pdu_type, attOpcode, st_hd );
            return;
        }
#else
        if(GetPrimaryServiceHandle(st_hd,end_hd,Value,&hd_s,&hd_e)) //porting api
        {
            _attData1[5] = hd_s;
            _attData1[7] = hd_e;
            att_rsp(hdr, _attData1);
        }
        else att_notFd( pdu_type, attOpcode, st_hd );  
        
        return ;
#endif
    }
    
    att_notFd( pdu_type, attOpcode, st_hd );/// error handle
}

void att_server_findIn( u8 pdu_type, u8 attOpcode, u16 st_hd, u16 end_hd )
{
    u16 l2cap_len = 6+4;
    u8 _attData1[10+4+4];
    u8 i;
    pdu_hdr_t hdr;

    hdr.type = (pdu_type & 0x03);
    hdr.len = 10;
    l2cap_len = 6;
    _attData1[1] = l2cap_len >> 8;
    _attData1[0] = l2cap_len & 0xFF;
    _attData1[3] = chanId >> 8;
    _attData1[2] = chanId & 0xFF;
    _attData1[4] = ATT_FIND_INFO_RSP;
    _attData1[5] = 0x01; //16 bit uuid
//    _attData1[6] = 0;	          //// handle
    _attData1[7] = 0;		
//    _attData1[8] = 0x02; 
//    _attData1[9] = 0x29;
    
    for(i=0; i<GetCharListDim()/*sizeof(AttCharList)/sizeof(AttCharList[0])*/; i++)
    {
        if((AttCharList[i].handle_rec >= st_hd) && (AttCharList[i].handle_rec <= end_hd))
        {
            _attData1[6] = AttCharList[i].handle_rec & 0xFF;
            _attData1[8] = AttCharList[i].type16 & 0xFF; 
            _attData1[9] = (AttCharList[i].type16 >> 8)& 0xFF;
            att_rsp(hdr, _attData1);
            return;
        }
    }
    
    ///error handle
    att_notFd(pdu_type, attOpcode, st_hd);
}

void ser_write_rsp_pkt(u8 pdu_type)
{
    //u16 l2cap_len = 1;
    u8 _attData1[5] = {0x01,0x00, 0x04,0x00, ATT_WR_RSP};
    pdu_hdr_t hdr;
    
    hdr.type = (pdu_type & 0x03);
    hdr.len = 5;
            
    ///to l2cap
    att_rsp(hdr, _attData1);
}

void gatt_user_ancs_report(u16 handle, u8 AncsID); //used for ANCS only

void MgBle_GattProc_Periph(u8* l2cap_pdu/*including header*/, u16 _len/*including header*/)
{
    u8 dataHdrP = l2cap_pdu[0] & 0x03; //LLID
    u8 attOpcode = l2cap_pdu[6]; ///att opcode
    att_incl_desc att_incl;
    u16 attHandle;
    u8 valueLen_w;
    u8* attValue = 0;

    switch(attOpcode){
        case ATT_EXCHANGE_MTU_REQ :
            att_exMtuReq(dataHdrP, attOpcode, l2cap_pdu[7] | (l2cap_pdu[8]<<8));
            break;

        case ATT_EXCHANGE_MTU_RSP:
            AttMtuSize = l2cap_pdu[7] | (l2cap_pdu[8]<<8);
            if(AttMtuSize > (LongPktMaxTxOctets - 4))AttMtuSize = LongPktMaxTxOctets - 4;
            break;

        case ATT_FIND_INFO_REQ ://info req
            att_incl.start_hdl = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            att_incl.end_hdl = l2cap_pdu[9] + (l2cap_pdu[10] << 8);
            if(att_incl.start_hdl > att_incl.end_hdl){ //GATT/SR/GAR/BI-08-C
                att_ErrorFd_eCode(dataHdrP, attOpcode, att_incl.start_hdl, ATT_ERR_INVALID_HANDLE);
                return;
            }
            att_server_findIn( dataHdrP, attOpcode, att_incl.start_hdl, att_incl.end_hdl );
            break;

        case ATT_FIND_BY_TYP_REQ:
            att_incl.start_hdl = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            att_incl.end_hdl = l2cap_pdu[9] + (l2cap_pdu[10] << 8);
            if(att_incl.start_hdl > att_incl.end_hdl){ //GATT/SR/GAR/BI-08-C
                att_ErrorFd_eCode(dataHdrP, attOpcode, att_incl.start_hdl, ATT_ERR_INVALID_HANDLE);
                return;
            }
            {
                u16 AttType,Value;
                AttType = l2cap_pdu[11] + (l2cap_pdu[12] << 8);
                Value = l2cap_pdu[13] + (l2cap_pdu[14] << 8);
                att_server_findByType(dataHdrP, attOpcode, att_incl.start_hdl, att_incl.end_hdl, AttType, Value);
            }
            break;

        case ATT_RD_BY_TYPE_REQ :
            att_incl.start_hdl = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            att_incl.end_hdl = l2cap_pdu[9] + (l2cap_pdu[10] << 8);
            att_incl.uuid   = l2cap_pdu[11] + (l2cap_pdu[12] << 8);
            if(att_incl.start_hdl > att_incl.end_hdl){ //GATT/SR/GAR/BI-08-C
                att_ErrorFd_eCode(dataHdrP, attOpcode, att_incl.start_hdl, ATT_ERR_INVALID_HANDLE);
                return;
            }
            att_server_rdByType(dataHdrP, attOpcode, att_incl.start_hdl, att_incl.end_hdl, att_incl.uuid);
            break;
            
        case ATT_RD_BY_GROUP_TYPE_REQ :
            att_incl.start_hdl = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            att_incl.end_hdl = l2cap_pdu[9] + (l2cap_pdu[10] << 8);
            att_incl.uuid   = l2cap_pdu[11] + (l2cap_pdu[12] << 8);
            if(att_incl.start_hdl > att_incl.end_hdl){ //GATT/SR/GAR/BI-08-C
                att_ErrorFd_eCode(dataHdrP, attOpcode, att_incl.start_hdl, ATT_ERR_INVALID_HANDLE);
                return;
            }
            att_server_rdByGrType( dataHdrP, attOpcode, att_incl.start_hdl, att_incl.end_hdl, att_incl.uuid );
            break;

        case ATT_RD_REQ :
            attHandle = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            server_rd_rsp(attOpcode, attHandle, dataHdrP);
            break;
        case ATT_READ_BLOB_REQ:
            attHandle = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            {
                void server_blob_rd_rsp(u8 attOpcode, u16 attHandle, u8 dataHdrP,u16 offset);//porting api
                u16 offset;
                offset = l2cap_pdu[9] + (l2cap_pdu[10] << 8);
                server_blob_rd_rsp(attOpcode, attHandle, dataHdrP,offset);
            }
            break;

        case ATT_WR_REQ :
            valueLen_w = l2cap_pdu[2] + (l2cap_pdu[3] << 8) - 3;//l2caplen - opcode(1) - attHandle(2)
            attHandle = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            attValue = &l2cap_pdu[9];
            ser_write_rsp(dataHdrP, attOpcode, attHandle, attValue, valueLen_w);
            break;
        case ATT_PREPARE_WR_REQ:
            attHandle = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
//                    if(attHandle > 0xff) {//GATT/SR/UNS/BI-01-C, unsupported ATT Requests on Server
//                        att_ErrorFd_eCode(dataHdrP, attOpcode, 0x0000, ATT_ERR_UNSUPPORTED_REQ);
//                    }else
            ser_prepare_write_rsp(l2cap_pdu);
            break;
        case ATT_EXECUTE_WR_REQ:
            ser_execute_write_rsp(l2cap_pdu);
            break;
        
        case ATT_WR_CMD :
            valueLen_w = l2cap_pdu[2] + (l2cap_pdu[3] << 8) - 3;//l2caplen - opcode(1) - attHandle(2)
            attHandle = l2cap_pdu[7] + (l2cap_pdu[8] << 8);
            attValue = &l2cap_pdu[9];
//                ser_write_no_rsp( dataHdrP, attOpcode, attHandle, attValue, valueLen_w);//org
            ser_write_rsp(dataHdrP, attOpcode, attHandle, attValue, valueLen_w);
            break;

        case ATT_CFM_HANDLE:
            break;
        
//////////////////////////////////////////ANCS usage /////////////////////////////////////////////////
        #define ANCS_CHAR_ID_NTF_SRC  0x00
        #define ANCS_CHAR_ID_CTL_PT   0x01
        #define ANCS_CHAR_ID_DAT_SRC  0x02
        #define ANCS_CHAR_ID_END      0x80
        #define ANCS_SERVICE_ID       0xFF
        #define ANCS_CHAR_ID_UNKNOWN  0xFE
        #define ANCS_CHAR_ID_UNKNOWN2 0xFD
        
        case ATT_FIND_BY_TYP_RSP: //primary ancs service
            {
                extern void save_ancs_service_handle_info(u16 _st, u16 _end);
                
                u16 _st,_end;
                _st = l2cap_pdu[8];
                _st <<= 8;
                _st |= l2cap_pdu[7];
                
                _end = l2cap_pdu[10];
                _end <<= 8;
                _end |= l2cap_pdu[9];
                
                save_ancs_service_handle_info(_st,_end);
                gatt_user_ancs_report(0,ANCS_SERVICE_ID);
            }
            break;
        case ATT_RD_BY_TYPE_RSP: //ancs character, hard code
            {
                void update_ancs_char_st_handle(u16 handle);
                u16 _st;
                
                if((l2cap_pdu[4] != 0x04) || (l2cap_pdu[5]))break;//unknown cid
                
                _st = l2cap_pdu[12];
                _st <<= 8;
                _st |= l2cap_pdu[11];

                update_ancs_char_st_handle(_st);
                
                if(l2cap_pdu[7] != 0x15) //len
                {
                    gatt_user_ancs_report(_st,ANCS_CHAR_ID_UNKNOWN);
                }
                else
                {
                    //check the uuid128
                    if((l2cap_pdu[13] == 0xD9) && (l2cap_pdu[14] == 0xD9)) //Control Point: UUID 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9 (writeable with response)
                    {
                        gatt_user_ancs_report(_st,ANCS_CHAR_ID_CTL_PT);
                    }
                    else if((l2cap_pdu[13] == 0xBD) && (l2cap_pdu[14] == 0x1D)) //Notification Source: UUID 9FBF120D-6301-42D9-8C58-25E699A21DBD (notifiable)
                    {
                        gatt_user_ancs_report(_st,ANCS_CHAR_ID_NTF_SRC);
                    }
                    else if((l2cap_pdu[13] == 0xFB) && (l2cap_pdu[14] == 0x7B)) //Data Source: UUID 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB (notifiable)
                    {
                        gatt_user_ancs_report(_st,ANCS_CHAR_ID_DAT_SRC);
                    }
                    else
                    {
                        gatt_user_ancs_report(_st,ANCS_CHAR_ID_UNKNOWN);
                    }                        
                }
            }
            break;
        case ATT_ERROR_RSP:
            switch(l2cap_pdu[7]) //req code
            {
                case ATT_FIND_BY_TYP_REQ: //primary service not found
                    gatt_user_ancs_report(0,ANCS_CHAR_ID_END);
                    break;
                case ATT_RD_BY_TYPE_REQ: //char discover end
                    gatt_user_ancs_report(0,ANCS_CHAR_ID_END);
                    break;
            }
            break;
            
        case ATT_WR_RSP:
            break;
        
        case ATT_NOTIFY_HANDLE:
            {
                void gatt_user_ancs_msg(u16 handle, u8*data, u8 len);
                gatt_user_ancs_msg(l2cap_pdu[7] + (l2cap_pdu[8] << 8),l2cap_pdu+9,l2cap_pdu[2]-3);
            }
            break;
//////////////////////////////////////////END of ANCS/////////////////////////////////////////////////
        
        default:
            if(attOpcode & 0x40){//cmd, GATT/SR/UNS/BI-02-C, unsupported ATT Commands on Server
                //siliently ignore the command
//                        att_ErrorFd_eCode(u8 pdu_type, u8 attOpcode, u16 attHd, u8 errorCode);
//                        att_notFd( dataHdrP, attOpcode, l2cap_pdu[7] + (l2cap_pdu[8] << 8) );
            }else{ //req, GATT/SR/UNS/BI-01-C, unsupported ATT Requests on Server
                att_ErrorFd_eCode(dataHdrP, attOpcode, 0x0000, ATT_ERR_UNSUPPORTED_REQ);
            }
            break;
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//               Notify/Indication/Write APIs
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
u8 gatt_notifydata(u16 Handle,u8* Buf, u8 Len)
{
    u8 max_size;    
    
    if(is_att_LongPktDataEnabled())
    {
        max_size = att_get_sconn_notifyLongPktMaxSize() - 4;
        if (Len >= att_get_sconn_notifyLongPktMinSize())
        {
            if (sconn_notifyLongPktData(Handle,Buf,Len>max_size?max_size:Len))
            {
                return Len>max_size?max_size:Len;
            }
            else
            {
                return 0;
            }
        }
        else
        {
            set_notifyhandle(Handle);
            return sconn_notifydata(Buf,Len);
        }
    }
    else
    {
        set_notifyhandle(Handle);
        return sconn_notifydata(Buf,Len);
    }
}

u8 gatt_indicationdata(u16 Handle,u8* Buf, u8 Len)
{
    u8 max_size;    
    
    if(is_att_LongPktDataEnabled())
    {
        max_size = att_get_sconn_indicationLongPktMaxSize() - 4;
        if (Len >= att_get_sconn_indicationLongPktMinSize())
        {
            if (sconn_indicationLongPktData(Handle,Buf,Len>max_size?max_size:Len))
            {
                return Len>max_size?max_size:Len;
            }
            else
            {
                return 0;
            }
        }
        else
        {
            set_notifyhandle(Handle);
            return sconn_indicationdata(Buf,Len);
        }
    }
    else
    {
        set_notifyhandle(Handle);
        return sconn_indicationdata(Buf,Len);
    }
}

void gatt_read_rsp(u8 pdu_type, u8 attOpcode, u16 att_hd, u8* attValue, u8 datalen )
{
    u8 max_size;
    
    if(datalen <= 22)
    {
        att_server_rd(pdu_type, attOpcode, att_hd, attValue, datalen);
    }
    else
    {
        if(is_att_LongPktDataEnabled())
        {
            max_size = att_get_sconn_notifyLongPktMaxSize() - 4;
            if (datalen >= att_get_sconn_notifyLongPktMinSize()+2)
            {
                att_server_rdLongPktData(attValue,datalen>max_size ? max_size:datalen); //one may check the status if any.
            }
            else//please change the data size
            {
                att_server_rd(pdu_type, attOpcode, att_hd, attValue, datalen); //WARNING!!I just shorten the data!!!!!!
            }
        }
        else//WARNING!!!I just shorten the data!!!!!!
        {
            att_server_rd(pdu_type, attOpcode, att_hd, attValue, 22);
        }
    }
}

u8 gatt_writedata(u16 writehandle,u8* data, u8 len)
{
    if(LongPktMaxTxOctets == 27)
    {
        return mconn_writedata(writehandle,data, len);
    }
    else
    {
        if(len < 25)
        {
            return mconn_writedata(writehandle,data, len);
        }
        
        //one may check mtu first!
        //to do...
        if(mconn_writeLongPktData(writehandle,data, len>(LongPktMaxTxOctets-7)?(LongPktMaxTxOctets-7):len))
        {
            return (len>(LongPktMaxTxOctets-7)?(LongPktMaxTxOctets-7):len);
        }
        else
        {
            return 0;
        }
    }    
}

//////////////////////////////ANCS app code//////////////////////////////////////////

#define ANCS_CHAR_ID_NTF_SRC  0x00
#define ANCS_CHAR_ID_CTL_PT   0x01
#define ANCS_CHAR_ID_DAT_SRC  0x02
#define ANCS_CHAR_ID_END      0x80
#define ANCS_CHAR_ID_UNKNOWN  0xFE
#define ANCS_SERVICE_ID       0xFF

//zeor means not found
u16 hd_ntf_src   = 0; 
u16 hd_dat_src   = 0;
u16 hd_ctl_point = 0;

void reset_ancs_par(void)
{
    hd_ntf_src   = 0; 
    hd_dat_src   = 0;
    hd_ctl_point = 0;
}

#define GATT_CLIENT_STATE_NONE          0x00
#define GATT_CLIENT_STATE_START         0xFF //find the service
#define GATT_CLIENT_STATE_DISCOVER_CHAR 0x01

void gatt_write_req(u16 hd, u16 data);

void gatt_user_ancs_msg(u16 handle, u8*data, u8 len)//call back api
{
    if(handle != hd_ntf_src) return; //just parser the notify source info
    if(len != 8) return;//format error

    // EventID|EventFlag|CategoryID|CategoryCount|UID32|
    
    switch(data[0]) //EventID
    {
        case 0x00:
//            printf("Added - ");
            break;
        
        case 0x01:
//            printf("Modified - ");
            break;
        
        case 0x02:
//            printf("Removed - ");
            break;
        default:break;
    }
    
    switch(data[2]) //CategoryID
    {
        case 0x00:
//            printf("Other\n");
            break;
        
        case 0x01:
//            printf("IncomingCall\n");
            break;
        
        case 0x02:
//            printf("MissedCall\n");
            break;
        
        case 0x03:
//            printf("Voicemail\n");
            break;
        
        case 0x04:
//            printf("Social\n"); //weixin sms
            break;
        
        case 0x05:
//            printf("Schedule\n");
            break;
        
        case 0x06:
//            printf("Email\n");
            break;
        
        case 0x07:
//            printf("News\n");
            break;
        
        case 0x08:
//            printf("HealthAndFitness\n");
            break;
        
        case 0x09:
//            printf("BusinessAndFinance\n");
            break;
        
        case 0x0a:
//            printf("Location\n");
            break;
        
        case 0x0b:
//            printf("Entertainment\n");
            break;
        
        default:break;
    }
//    printf("\n");
        
}

void gatt_user_ancs_report(u16 handle, u8 AncsID)//callback api
{
    void ANCS_DiscoverMore(void);
    
    switch(AncsID)
    {        
        case ANCS_CHAR_ID_NTF_SRC: //found notify source(mardarine)
            hd_ntf_src = handle;
//            printf("ANCS_CHAR_ID_NTF_SRC:0x%02x\n",hd_ntf_src);
            break;
        case ANCS_CHAR_ID_CTL_PT: //found control point
            hd_ctl_point = handle;
//            printf("ANCS_CHAR_ID_CTL_PT:0x%02x\n",hd_ctl_point);
            break;
        case ANCS_CHAR_ID_DAT_SRC: //found data source
            hd_dat_src = handle;
//            printf("ANCS_CHAR_ID_DAT_SRC:0x%02x\n",hd_dat_src);
            break;
        case ANCS_SERVICE_ID: //found ancs service
//            printf("found ANCS_SERVICE_ID\n");
            break;
        
        case ANCS_CHAR_ID_END: //end of discover
//            printf("ANCS end.\n");
        
            //enable the notify charactres if any...
            if(hd_ntf_src)
            {
                gatt_write_req(hd_ntf_src+1,0x0001);
            }
            if(hd_dat_src)
            {
                gatt_write_req(hd_dat_src+1,0x0001);
            }
        
            return; //end of ANCS discover stage
            //break;
        
        default:
//            printf("default:0x%02x(0x%04x)\n",AncsID,handle);
            break;
    }
    
    ANCS_DiscoverMore(); //discover more if any
}
////////////////////////////////End of ANCS app code//////////////////////////////////////////
